package main

import (
	"fmt"
	"reflect"
)

/*
	反射 API 的分类总结如下：

	1) 从实例到 Value
		通过实例获取 Value 对象，直接使用 reflect.ValueOf() 函数。
		func ValueOf(i interface {}) Value

	2) 从实例到 Type
		通过实例获取反射对象的 Type，直接使用 reflect.TypeOf() 函数。
		func TypeOf(i interface{}) Type

	3) 从 Type 到 Value
		Type 里面只有类型信息，所以直接从一个 Type 接口变量里面是无法获得实例的 Value 的，但可以通过该 Type 构建一个新实例的 Value

		func New(typ Type) Value   //New 返回的是一个Value，该Value的type为PtrTo(typ)，即Value的Type是指定typ的指针类型

		func Zero(typ Type) Value  //Zero 返回的是一个typ类型的零佳，注意返回的Value不能寻址，位不可改变

	4) 从 Value 到 Type
		从反射对象 Value 到 Type 可以直接调用 Value 的方法，因为 Value 内部存放着到 Type 类型的指针。
		func (v Value) Type() Type

	5) 从 Value 到实例
		Value 本身就包含类型和值信息，reflect 提供了丰富的方法来实现从 Value 到实例的转换。

		func (v Value) Interface() （i interface{}) //该方法最通用，用来将 Value 转换为空接口，该空接口内部存放具体类型实例

	6) 从 Value 的指针到值
		从一个指针类型的 Value 获得值类型 Value 有两种方法.

		//如果v类型是接口，则Elem()返回接口绑定的实例的Value，若v类型是指针，则返回指针值的Value，否则引起panic
		func (v Value) Elem() Value

		//如果v是指针，则返回指针值的Value，否则返回v自身，该函数不会引起panic
		func Indirect(v Value) Value

	7) Type 指针和值的相互转换
		指针类型 Type 到值类型 Type
    	//t 必须是 Array、Chan、Map、Ptr、Slice，否则会引起 panic
		//Elem 返回的是其内部元素的 Type
		t.Elem() Type

		值类型 Type 到指针类型 Type
		//PtrTo 返回的是指向 t 的指针型 Type
		func PtrTo(t Type) Type

	8) Value 值的可修改性
		Value 值的修改涉及如下两个方法：
		//通过 CanSet 判断是否能修改
		func (v Value ) CanSet() bool
		//通过 Set 进行修改
		func (v Value ) Set(x Value)


反射有三大定律:
	第一条是最基本的：反射可以从接口值得到反射对象。
	反射是一种检测存储在 interface中的类型和值机制。这可以通过 TypeOf函数和 ValueOf函数得到。

	第二条实际上和第一条是相反的机制，反射可以从反射对象获得接口值。
	它将 ValueOf的返回值通过 Interface()函数反向转变成 interface变量。

	前两条就是说 接口型变量和 反射类型对象可以相互转化，反射类型对象实际上就是指的前面说的 reflect.Type和 reflect.Value。

	第三条：如果需要操作一个反射变量，则其值必须可以修改。


通过运行结果可以得知获取未知类型的interface的具体变量及其类型的步骤为：

	1.先获取interface的reflect.Type，然后通过NumField进行遍历
	2.再通过reflect.Type的Field获取其Field
	3.最后通过Field的Interface()得到对应的value

通过运行结果可以得知获取未知类型的interface的所属方法（函数）的步骤为：

	1.先获取interface的reflect.Type，然后通过NumMethod进行遍历
	2.再分别通过reflect.Type的Method获取对应的真实的方法（函数）
	3.最后对结果取其Name和Type得知具体的方法名
	4.也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
	5.struct 或者 struct 的嵌套都是一样的判断处理方式

*/

type Person struct {
	Name string
	Age int
	Sex string
}

func (p Person) Say (msg string) {
	fmt.Println("hello，",msg)
}

func (p Person) PrintInfo() {
	fmt.Printf("姓名：%s,年龄：%d，性别：%s\n",p.Name,p.Age,p.Sex)
}

func main() {

	var num float64 = 1.2345

	pointer := reflect.ValueOf(&num)

	value :=reflect.ValueOf(num)

	// 可以理解为“强制转换”，但是需要注意的时候，转换的时候，如果转换的类型不完全符合，则直接panic
	// Golang 对类型要求非常严格，类型一定要完全符合
	// 如下两个，一个是*float64，一个是float64，如果弄混，则会panic
	convertPtr :=pointer.Interface().(*float64)
	convertVal :=value.Interface().(float64)

	fmt.Println(convertPtr)
	fmt.Println(convertVal)

	person := Person{"zhangsan",18,"男"}

	DoFiledAndMethod(person)

	
}

// 通过接口来获取任意参数
func DoFiledAndMethod(in interface{}) {

	//先获取input的类型
	typeOf := reflect.TypeOf(in)

	fmt.Println("get type is :", typeOf.Name())
	fmt.Println("get kind is :", typeOf.Kind())

	valOf := reflect.ValueOf(in)
	fmt.Println("get all Fields is:", valOf)

	// 获取方法字段
	// 1. 先获取interface的reflect.Type，然后通过NumField进行遍历
	// 2. 再通过reflect.Type的Field获取其Field
	// 3. 最后通过Field的Interface()得到对应的value

	for i :=0; i< typeOf.NumField(); i++ {
		field := typeOf.Field(i)
		val := valOf.Field(i).Interface()
		fmt.Printf("字段名称:%s, 字段类型:%s, 字段数值:%v \n", field.Name, field.Type, val)
	}

	for i :=0; i< typeOf.NumMethod(); i++ {
		method := typeOf.Method(i)
		fmt.Printf("方法名称:%s, 方法类型:%v \n", method.Name, method.Type)
	}

}
